#coding=utf-8

import re
import datetime
import time
#將一串字串解析為「時間」或「時間段」。

#format中的tuple中，第一個代表格式，
#第二個代表解析成功後，要用哪些now內的東西取代掉：
#Y:年，M:月，W:週，D:日，h:時，m:分，s:秒
#第三個代表誤差值所在，格式同上
timeFormats = (
        ('%d', 'YM', 'D'),      #12         -> 今年今月12日

        ('%m-', 'Y', 'M'),      #10-        -> 今年10月
        ('%m-%d', 'Y', 'D'),    #10-20      -> 今年10月20日
        ('%y-%m-', '', 'M'),    #11-10-     -> 2011年10月
        ('%y-%m-%d', '', 'D'),  #11-10-20   -> 2011年10月20日

        ('%Y', '', 'Y'),        #2011       -> 2011年

        ('%Y-', '', 'Y'),       #2011-      -> 2011年
        ('%Y-%m', '', 'M'),     #2011-10    -> 2011年10月
        ('%Y-%m-', '', 'M'),    #2011-10-   -> 2011年10月
        ('%Y-%m-%d', '', 'D'),  #2011-10-20 -> 2011年10月20日

        ('%m/', 'Y', 'M'),      #10/        -> 今年10月
        ('%m/%d', 'Y', 'D'),    #10/20      -> 今年10月20日
        ('%y/%m/', '', 'M'),    #11/10/     -> 2011年10月
        ('%y/%m/%d', '', 'D'),  #11/10/20   -> 2011年10月20日

        ('%Y/', '', 'Y'),       #2011/      -> 2011年
        ('%Y/%m', '', 'M'),     #2011/10    -> 2011年10月
        ('%Y/%m/', '', 'M'),    #2011/10/   -> 2011年10月
        ('%Y/%m/%d', '', 'D'),  #2011/10/20 -> 2011年10月20日

        ('%yy', '', 'Y'),       #11y        -> 2011年

        ('%yy%m', '', 'M'),     #11y10      -> 2011年10月
        ('%yy%mm', '', 'M'),    #11y10m     -> 2011年10月
        ('%yy%mm%d', '', 'D'),  #11y10m20   -> 2011年10月20日
        ('%yy%mm%dd', '', 'D'), #11y10m20d  -> 2011年10月20日

        ('%mm', 'Y', 'M'),      #10m        -> 今年10月

        ('%mm%d', 'Y', 'D'),    #10m20      -> 今年10月20日
        ('%mm%dd', 'Y', 'D'),   #10m20d     -> 今年10月20日

        ('%dd', 'YM', 'D'),     #12d        -> 今年今月12日
        )

#用做beforeMode的格式表，
#第一個是用來parsing的regex，其中會有一個數字值
#第二個參數是這個數字值應該被解譯為什麼
#Y:年，M:月，W:週，D:日，h:時，m:分，s:秒
beforeModeTimeFormats = (
        (r'^(\d+)$', 'D'),       #2          -> 最近2天

        (r'^(\d+)d$', 'D'),      #2d         -> 最近2天
        (r'^(\d+)da$', 'D'),     #2da        -> 最近2天
        (r'^(\d+)day$', 'D'),    #2day       -> 最近2天
        (r'^(\d+)days$', 'D'),   #2days      -> 最近2天

        (r'^(\d+)w$', 'W'),      #2w         -> 最近2週
        (r'^(\d+)we$', 'W'),     #2we        -> 最近2週
        (r'^(\d+)wee$', 'W'),    #2wee       -> 最近2週
        (r'^(\d+)week$', 'W'),   #2week      -> 最近2週
        (r'^(\d+)weeks$', 'W'),  #2weeks     -> 最近2週

        (r'^(\d+)m$', 'M'),      #2m         -> 最近2月
        (r'^(\d+)mo$', 'M'),     #2mo        -> 最近2月
        (r'^(\d+)mon$', 'M'),    #2mon       -> 最近2月
        (r'^(\d+)mont$', 'M'),   #2mont      -> 最近2月
        (r'^(\d+)month$', 'M'),  #2month     -> 最近2月
        (r'^(\d+)months$', 'M'), #2months    -> 最近2月

        (r'^(\d+)y$', 'Y'),      #2y         -> 最近2年
        (r'^(\d+)ye$', 'Y'),     #2ye        -> 最近2年
        (r'^(\d+)yea$', 'Y'),    #2yea       -> 最近2年
        (r'^(\d+)year$', 'Y'),   #2year      -> 最近2年
        (r'^(\d+)years$', 'Y'),  #2years     -> 最近2年

        (r'^(\d+)h$', 'h'),      #2h         -> 最近2小時
        (r'^(\d+)ho$', 'h'),     #2ho        -> 最近2小時
        (r'^(\d+)hou$', 'h'),    #2hou       -> 最近2小時
        (r'^(\d+)hour$', 'h'),   #2hour      -> 最近2小時
        (r'^(\d+)hours$', 'h'),  #2hours     -> 最近2小時
        )

def getTimeByString(string, timeFormats = timeFormats,
        bmTimeFormats = beforeModeTimeFormats):
    '''想辦法將string表達的時間值給確認出來
    回傳(datetime, 誤差值)物件。如果無法解析，回傳None'''

    def beforeModeString(string):
        '''測試字串是否為beforeMode，
        如果是，回傳去掉beforeMode標記的新字串，否則回傳空字串'''
        ans = ''
        if string.startswith('before'):
            ans = string[6:]
        elif string.startswith('bef'):
            ans = string[3:]
        elif string.startswith('b'):
            ans = string[1:]
        return ans

    def timeToStart(date ,err):
        '''將時間修到開頭'''
        if err == 'Y':
            return datetime.datetime(year = date.year, month = 1, day = 1)
        elif err == 'M':
            return datetime.datetime(year = date.year, month = date.month, day = 1)
        elif err == 'W': #周的計算以星期一為開始
            shift = datetime.timedelta(days = date.weekday())
            return datetime.datetime(year = date.year, month = date.month, day = date.day) - shift
        elif err == 'D':
            return datetime.datetime(year = date.year, month = date.month, day = date.day)
        elif err == 'h':
            return datetime.datetime(year = date.year, month = date.month, day = date.day, hour = date.hour)
        elif err == 'm':
            return datetime.datetime(year = date.year, month = date.month, day = date.day, hour = date.hour, minute = date.minute)
        elif err == 's':
            return datetime.datetime(year = date.year, month = date.month, day = date.day, hour = date.hour, minute = date.minute, second = date.second)

    #先預備基礎資料
    string = string.lower() #先全轉小寫
    now = datetime.datetime.now()

    #先進行beforeMode測試
    beforeStr = beforeModeString(string)
    if beforeStr:
        for bmtFormat, mode in bmTimeFormats:
            ans = re.findall(bmtFormat, beforeStr)
            if ans: #如果ans存在，開始計算數值的意思
                digit = int(ans[0])
                date = None
                if mode == 'Y':
                    date = now.replace(year = now.year - digit)
                elif mode == 'M':
                    #計算幾年前
                    date = now.replace(year = now.year - (digit // 12))
                    #計算11~0範圍內的幾個月前
                    month = digit % 12
                    if date.month < month: #會回退到去年11月前
                        date = date.replace(year = date.year - 1)
                        date = date.replace(
                                month = 12 - (month - date.month))
                    elif date.month == month: #會回退到去年12月
                        date = date.replace(year = date.year - 1)
                        date = date.replace(month = 12)
                    else: #就在今年
                        date = date.replace(month = date.month - month)
                elif mode == 'W':
                    date = now - datetime.timedelta(days = 7 * digit)
                elif mode == 'D':
                    date = now - datetime.timedelta(days = digit)
                elif mode == 'h':
                    date = now - datetime.timedelta(hours = digit)
                elif mode == 'm':
                    date = now - datetime.timedelta(minutes = digit)
                elif mode == 's':
                    date = now - datetime.timedelta(seconds = digit)
                #將修到該誤差值的開頭
                begin = timeToStart(date, mode)
                #回傳
                return (begin, mode)
        #跑完迴圈還找不到對應值，解析失敗……
        return None
    else:
        #如果不是before模式，進行絕對時間解析測試
        dt = None
        for timeFormat, replace, err in timeFormats:
            try:
                dt = [datetime.datetime.strptime(string, timeFormat), err]
                if 'Y' in replace:
                    dt[0] = dt[0].replace(year = now.year)
                if 'M' in replace:
                    dt[0] = dt[0].replace(month = now.month)
                if 'D' in replace:
                    dt[0] = dt[0].replace(day = now.day)
                if 'h' in replace:
                    dt[0] = dt[0].replace(hour = now.hour)
                if 'm' in replace:
                    dt[0] = dt[0].replace(minute = now.minute)
                if 's' in replace:
                    dt[0] = dt[0].replace(second = now.second)
                break
            except: pass
        return dt

def getTimeRangeByString(string):
    '''想辦法將string表達的時間區間給確認出來
    回傳(begintime, endtime)。如果無法解析，回傳None'''

    def ExtendError(dt, err):
        if 'Y' in err:
            dt = dt.replace(year = dt.year + 1)
        elif 'M' in err:
            if dt.month != 12:
                dt = dt.replace(month = dt.month + 1)
            else:
                dt = dt.replace(year = dt.year + 1, month = 1)
        elif 'W' in err:
            dt = dt + datetime.timedelta(days = 7)
        elif 'D' in err:
            dt = dt + datetime.timedelta(days = 1)
        elif 'h' in err:
            dt = dt + datetime.timedelta(hours = 1)
        elif 'm' in err:
            dt = dt + datetime.timedelta(minutes = 1)
        elif 's' in err:
            dt = dt + datetime.timedelta(seconds = 1)
        return dt

    if '~' in string:
        answer = string.split('~') #可能的格式為DDD~DDD
        begintext = answer[0]
        endtext = answer[1]
        #空白表示無限制 -> begin縮到最小，end放到最大
        if begintext == '':
            begin = (datetime.datetime(1970,1,1), '')
        else:
            begin = getTimeByString(begintext, timeFormats)
        if endtext == '':
            end = (datetime.datetime(2038,1,1), '') #超過這個值就會掛掉……
        else:
            end = getTimeByString(endtext, timeFormats)
        if begin:
            realbegin = begin[0]
        else: #begintext解碼不過則放棄
            return None
        if end:
            realend = ExtendError(end[0], end[1])
            if realbegin >= realend: #這顯然不正確！（前後顛倒了）
                #把前後對調然後再來一次
                revStr = endtext + '~' + begintext
                #print(revStr)
                return getTimeRangeByString(revStr)
            else:
                return (time.mktime(realbegin.timetuple()),
                        time.mktime(realend.timetuple()))
        else: #end解碼不過則放棄
            return None
    else: #只有一個值（沒有"~"時）
        begin = getTimeByString(string, timeFormats)
        if begin:
            realbegin = begin[0]
            realend = ExtendError(begin[0], begin[1])
            return (time.mktime(realbegin.timetuple()),
                    time.mktime(realend.timetuple()))
        else:
            return None

